/*
 * Copyright 2002-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.fjsei.yewu.flow;

import io.camunda.zeebe.client.ZeebeClient;
import md.cm.flow.ApprovalStm;
import md.cm.unit.Units;
import md.log.Opinion_Enum;
import md.specialEqp.Eqp;
import md.specialEqp.Report;
import md.specialEqp.inspect.*;
import md.system.User;
import org.fjsei.yewu.bpm.ApprovalStmService;
import org.fjsei.yewu.graphql.IdMapper;
import org.fjsei.yewu.resolver.Comngrql;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.MutationMapping;
import org.springframework.graphql.execution.BatchLoaderRegistry;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.List;

/**因为和zeebe流程相关的，ReportController部分接口拆分出去。@可独立控制加载#。
 * 底下批处理方案还不是一种的。 注释掉的也是1个方案。
 * 每一个graphQL的Object对象type都需单独声明一个XxxController implements IdMapper<type> {} 否则Id都无法转换成Relay要的GlobalID的。
 * */
//【报错】Annotation-specified bean name 'reportController' for bean class [org.fjsei.yewu.report.ReportController] org.fjsei.yewu.flow.ReportController]
@ConditionalOnBean(ZeebeClient.class)
@Controller
public class ReportFlowController extends Comngrql implements IdMapper<Report> {
//	private final Units units; 用基类

//	private final Companies companies;
//	private final TaskRepository tasks;
//	private final IspRepository isps;
	//private final ApprovalStmRepository  approvalStms; 基类已经注入该表了，就不要重复：两个实例。
//	private final ReportRepository  reports;
	private final ApprovalStmService approvalStmService;
	/** registry参数：IDEA报红却能运行成功，实际运行注入DefaultBatchLoaderRegistry;
     *  消除报红，测试平台Units没有报红 https://blog.csdn.net/zy103118/article/details/84523452
	 * 添加了 @ComponentScan({"md","org.fjsei.yewu"}) 以后: Units报红消除了。
	 * */
	public ReportFlowController(BatchLoaderRegistry registry, Units units, ApprovalStmService approvalStmService) {
		this.units = units;
//		this.companies = companies;
//		this.tasks = tasks;
//		this.isps = isps;
//		this.reports = reports;
		this.approvalStmService = approvalStmService;
	}

	/**报告编制结束，进入zeebe流程: 开始多人签字
     * 流转 审批过程： 针对出报告的。
	 * 状态步骤环节预定义好的，外部逻辑控制流程，流转历史回撤按时间和环节标记来追溯后退，光靠ApprovalStm无法支持流程控制。
	 * 尽量抽象？ 做成通用的接口。 授权也不一样啊！ ？ 用户权限控制还得关联实际接口  没法整。
	 * 已经走过流程实例的情况：修改报告后重新流转的请使用flowTo()接口。
	 * */
	@MutationMapping
	@Transactional
	public String  flowReport(@Argument String repId,@Argument String uri,@Argument Opinion_Enum allow,@Argument String memo,@Argument Integer days) {
		User user=checkAuth();
		if(null==user)  return "没登录";
		//Report report = reportRepository.findById(repId).orElse(null);
		Report report = entityOf(repId,Report.class);
		if(null==report)    return "没找到报告";
		//ApprovalStm可以保存历史配置，不是在本命令接口提供所有的预设的参数。
		ApprovalStm stm=report.getStm();	  //仅仅是状态和历史操作记录，不是真正的流程引擎服务。
		if(null==stm)   return "没状态机";
		Assert.isTrue(user.equals(stm.getMaster()),"校核人才能开启流程");
		Assert.isTrue(null==stm.getPrId(),"已有流程");
		Assert.isTrue(Procedure_Enum.BEGIN.equals(stm.getSta()) || Procedure_Enum.MAKE.equals(stm.getSta()),"已流转");
		Boolean subitem=true;
		if(report.getIsp().getReport()==report)		subitem=false;
		//流转时刻 每个阶段都要单独的验证 :人工审查+ 自动校对+触发业务功能。
//		Set<User> users=new HashSet<>();
//		userIds.forEach(item -> {	 	//todo:剔除校核人自己
//			User man = entityOf(item,User.class);
//			//User man=userRepository.findById(item).orElse(null);
//			Assert.isTrue(man != null,"未找到user:"+item);
//			users.add(man);
//		});		if(users.size()<=0)  return "授权用户为空";	  //只有一人还是校核人的情况？ 待签名人?空集
		Eqp eqp= report.getIsp().getDev();
		String ident;
		if(eqp==null) 	ident= report.getIsp().getBus().getIdent();
		else 	ident= StringUtils.hasText(eqp.getCod())? eqp.getCod(): eqp.getOid();
		String topic= ident+"报告"+report.getIsp().getNo();

		String processId= approvalStmService.startFlow("ReportApproval",topic,repId, uri, stm, allow, memo, days, stm.getAuthr(), subitem);
		if(null==processId)		//关键的Process Instance Id，@@要避免遗忘掉已经生成的流程！zeebe引擎存储的遗漏占用;
			return "移交失败";
		return  processId;
	}
	/**处理zeebe服务的UserTask列表， 完成后当前用户 确认审批或者回退等操作或签字过关。
	 * 修改报告后重新再一次结束编制状态这时刻校核人员有权再次重新流转的也用这个接口,而不是全新未发生流程行为的flowReport()。
	 * */
	@MutationMapping
	@Transactional
	public String  flowTo(@Argument String entId, @Argument String userTaskId, @Argument Opinion_Enum allow, @Argument String memo,
						  @Argument List<String> mens, @Argument Integer days, @Argument String uri) {
		//todo: 不一定都得是Report实体类。 申请单都行;
		Report report = entityOf(entId,Report.class);   //或者其他的申请单实体 都 instance of FlowStat
		if(null==report)    return "没找到报告";
		//??直接从zeebe服务提取 流程变量：统一流程变量ent 代表挂接的申请单或报告的实体表ID: GlobalID;
		//应当把关联的Report报告id给我传递进来啊，写入流程日志实体表也得用它。普通申请单的实体模型对象也有ID。
		String rUserTaskId= approvalStmService.flowTo(userTaskId, report, allow, memo,days, null,uri);
		if(null==rUserTaskId)	return "移交失败";
		return  rUserTaskId;
	}

}

